package com.wangjiacai.utils.ali;

import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SpeechReqProtocol;
import com.alibaba.nls.client.util.IdGen;
import com.wangjiacai.utils.ali.asr.SpeechTranscriber;
import com.wangjiacai.utils.ali.tts.SpeechSynthesizer;
import com.wangjiacai.utils.ali.tts.Voice;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static com.alibaba.nls.client.protocol.SpeechReqProtocol.State.STATE_STOP_SENT;


@Slf4j
@Data
public class NettyConnection {

    private String appKey;
    private String currentTaskId;
    private Channel channel;
    private AliPool aliPool;
    private NettySpeechListener nettySpeechTranscriberListener;
    private long connectingLatency;
    private long handshakeLatency;
    private CountDownLatch completeLatch;
    private CountDownLatch readyLatch;
    private SpeechReqProtocol.State state;

    public NettyConnection(Channel channel, long connectingLatency,long handshakeLatency) {
        this.channel = channel;
        this.connectingLatency = connectingLatency;
        this.handshakeLatency = handshakeLatency;
    }

    public long getWsHandshakeLatency() {
        return this.handshakeLatency;
    }
    public String getId() {
        return this.channel != null ? this.channel.id().toString() : null;
    }

    public boolean isActive() {
        return this.channel != null && this.channel.isActive();
    }

    public void close() {
        aliPool.free(this);
    }

    public void sendText(String payload) {
        if (this.channel != null && this.channel.isActive()) {
            log.debug("thread:{},send:{}", Thread.currentThread().getId(), payload);
            TextWebSocketFrame frame = new TextWebSocketFrame(payload);
            this.channel.writeAndFlush(frame);
        }
    }

    public void sendBinary(byte[] payload) {
        if (this.channel != null && this.channel.isActive()) {
            BinaryWebSocketFrame frame = new BinaryWebSocketFrame(Unpooled.wrappedBuffer(payload));
            this.channel.writeAndFlush(frame);
        }
    }

    public void sendPing() {
        PingWebSocketFrame frame = new PingWebSocketFrame();
        if (this.channel != null && this.channel.isActive()) {
            this.channel.writeAndFlush(frame);
        }
    }

    public void sendTtsText(String text){
        this.sendTtsText(text,OutputFormatEnum.WAV,Voice.StandWomenVoiceXY);
    }

    public void sendTtsText(String text,OutputFormatEnum format){
        this.sendTtsText(text,format,Voice.StandWomenVoiceXY);
    }

    public void sendTtsText(String text,OutputFormatEnum format, Voice voice){
        SpeechSynthesizer speechSynthesizer=new SpeechSynthesizer(appKey);
        speechSynthesizer.setVoice(voice.getVoice());
        speechSynthesizer.setFormat(format);
        speechSynthesizer.setText(text);
        String taskId = IdGen.genId();
        speechSynthesizer.setTaskId(taskId);
        this.sendText(speechSynthesizer.serialize());
    }

    public void stop() throws Exception{
        SpeechReqProtocol req = new SpeechReqProtocol();
        req.header.put("task_id", this.currentTaskId);
        req.header.put("namespace", "SpeechTranscriber");
        req.header.put("name", "StopTranscription");
        req.setAppKey(this.getAppKey());
        this.sendText(req.serialize());
        state = STATE_STOP_SENT;
        boolean result = completeLatch.await(10, TimeUnit.SECONDS);
        if (!result) {
            String msg = String.format("timeout after 10 seconds waiting for complete confirmation.task_id:%s,state:%s",
                    currentTaskId, state);
            log.error(msg);
            throw new Exception(msg);
        }
    }

    public void start(SpeechTranscriber speechTranscriber) throws Exception {
        this.sendText(speechTranscriber.serialize());
        this.setCurrentTaskId(speechTranscriber.getTaskId());
        this.setAppKey(speechTranscriber.getAppKey());
        completeLatch = new CountDownLatch(1);
        readyLatch = new CountDownLatch(1);
        boolean result = readyLatch.await(10, TimeUnit.SECONDS);
        if (!result) {
            String msg = String.format("timeout after 10 seconds waiting for start confirmation.task_id:%s,state:%s",currentTaskId,state);
            log.error(msg);
            throw new Exception(msg);
        }
    }

    @Override
    public String toString() {
        return "NettyConnection{" +
                "appKey='" + appKey + '\'' +
                ", channel=" + channel +
                ", aliPool=" + aliPool +
                '}';
    }

    public void markTranscriberReady() {
        this.state = SpeechReqProtocol.State.STATE_REQUEST_CONFIRMED;
        if (this.readyLatch != null) {
            this.readyLatch.countDown();
        }

    }

    public void markTranscriberComplete() {
        this.state = SpeechReqProtocol.State.STATE_COMPLETE;
        if (this.completeLatch != null) {
            this.completeLatch.countDown();
        }
    }

    public void markFail() {
        this.state = SpeechReqProtocol.State.STATE_FAIL;
        if (this.readyLatch != null) {
            this.readyLatch.countDown();
        }

        if (this.completeLatch != null) {
            this.completeLatch.countDown();
        }

    }

    public void markClosed() {
        this.state = SpeechReqProtocol.State.STATE_CLOSED;
        if (this.readyLatch != null) {
            this.readyLatch.countDown();
        }

        if (this.completeLatch != null) {
            this.completeLatch.countDown();
        }

    }
}
